#include <av_player/qls_av_player.h>

#include <QDebug>

#define SDL_AUDIO_BUFFER_SIZE 1024
#define AVCODEC_AVCODEC_MAX_AUDIO_FRAME_SIZE \
    192000 // 1 second of 48khz 32bit audio
#define FLUSH_DATA "FLUSH"

typedef unsigned long DWORD;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long ULONG_PTR;
typedef ULONG_PTR DWORD_PTR;

#define MAKEWORD(a, b)                          \
    ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | \
            ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define LOBYTE(w) ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w) ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

/* 根据解码后的视频数据 计算出视频的pts */
double SynchronizeVideo(QLsAVParam *avParam, AVFrame *srcFrame, double pts)
{
    double frameDelay;

    /* 如果有pts，设置视频时钟 */
    if (pts != 0)
        avParam->m_vClock = pts;
    /* 如果没有pts，获取视频时钟 */
    else
        pts = avParam->m_vClock;

    /* 更新视频时钟 */
    frameDelay = av_q2d(avParam->m_vStream->time_base);
    /* 如果重复一个帧，相应地调整时钟 */
    frameDelay += srcFrame->repeat_pict * (frameDelay * 0.5);
    avParam->m_vClock += frameDelay;
    return pts;
}

/* 音量调节 */
void RaiseVolume(char *buf, int size, int uRepeat, double vol)
{
    if (!size)
        return;

    for (int i = 0; i < size; i += 2) {
        short wData = MAKEWORD(buf[i], buf[i + 1]);
        long dwData = wData;
        for (int j = 0; j < uRepeat; j++) {
            dwData = dwData * vol;
            if (dwData < -0x8000)
                dwData = -0x8000;
            else if (dwData > 0x7FFF)
                dwData = 0x7FFF;
        }
        wData = LOWORD(dwData);
        buf[i] = LOBYTE(wData);
        buf[i + 1] = HIBYTE(wData);
    }
}

/* 视频解码线程 */
int VDecodeThread(void *arg)
{
    QLsAVParam *avParam = (QLsAVParam *)arg;
    AVPacket pkt1, *packet = &pkt1;
    double video_pts = 0; /* 当前视频的pts */
    double audio_pts = 0; /* 音频pts */

    /* 参数初始化 */
    avParam->m_bVDecodeThreadFinished = false;

    /* 解码视频相关 */
    AVFrame *pFrame = av_frame_alloc();
    AVFrame *pFrameRGB = av_frame_alloc();
    /* 解码后的rgb数据 */
    uint8_t *out_buffer_rgb = (uint8_t *)av_malloc(
        av_image_get_buffer_size(AV_PIX_FMT_RGB32, avParam->m_vCodecCtx->width,
                                 avParam->m_vCodecCtx->height, 1) *
        sizeof(uint8_t));
    /* 用于解码后的视频格式转换，将解码后的YUV数据转换成RGB32 */
    SwsContext *img_convert_ctx = sws_getContext(
        avParam->m_vCodecCtx->width, avParam->m_vCodecCtx->height,
        avParam->m_vCodecCtx->pix_fmt, avParam->m_vCodecCtx->width,
        avParam->m_vCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr,
        nullptr, nullptr);
    av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer_rgb,
                         AV_PIX_FMT_RGB32, avParam->m_vCodecCtx->width,
                         avParam->m_vCodecCtx->height, 1);

    bool bFrist = avParam->m_bFirstPause;
    for (;;) { /* 停止 */
        if (avParam->m_bQuit) {
            /* 清空队列 */
            if (avParam->m_vPktQueue) {
                avParam->m_vPktQueue->Clear();
            }
            break;
        }

        /* 暂停 */
        if (avParam->m_bPause == true) {
            SDL_Delay(10);
            continue;
        }

        /* 从队列中读取数据包 */
        if (!avParam->m_vPktQueue->PopFront(packet)) {
            /* 队列里面没有数据了 且 解码完毕了 */
            if (avParam->m_bDecodeFinished)
                break;
            /* 队列只是暂时没有数据而已 */
            else {
                SDL_Delay(1);
                continue;
            }
        }

        /* 收到这个数据 说明刚刚执行过跳转 现在需要把解码器的数据 清除一下 */
        if (strcmp((char *)packet->data, FLUSH_DATA) == 0) {
            avcodec_flush_buffers(avParam->m_vCodecCtx);
            av_packet_unref(packet);
            continue;
        }

        /* 向解码器中添加一包输入数据 */
        if (avcodec_send_packet(avParam->m_vCodecCtx, packet)) {
            qDebug() << "input AVPacket to decoder failed!";
            av_packet_unref(packet);
            continue;
        }

        /* 从解码器中获取已解码的输出数据 */
        while (!avcodec_receive_frame(avParam->m_vCodecCtx,
                                      pFrame)) { /* 获取视频pts */
            if (packet->dts == AV_NOPTS_VALUE && pFrame->opaque &&
                *(qint64 *)pFrame->opaque != AV_NOPTS_VALUE)
                video_pts = *(uint64_t *)pFrame->opaque;
            else if (packet->dts != AV_NOPTS_VALUE)
                video_pts = packet->dts;
            else
                video_pts = 0;

            video_pts *= av_q2d(avParam->m_vStream->time_base);
            video_pts = SynchronizeVideo(avParam, pFrame, video_pts);

            /* 发生了跳转 则跳过关键帧到目的时间的这几帧 */
            if (avParam->m_bVSeek) {
                if (video_pts < avParam->m_seekTime) {
                    av_packet_unref(packet);
                    continue;
                } else
                    avParam->m_bVSeek = false;
            }

            /* 音视频同步 */
            while (1) {
                /* 退出 */
                if (avParam->m_bQuit)
                    break;

                /* 有音频 */
                if (avParam->m_aStream &&
                    !avParam->m_bADecodeThreadFinished) { /* 解码完了 且
                                                             音频数据也播放完了
                                                             就剩下视频数据了
                                                             直接显示出来了
                                                             不用同步了 */
                    if (avParam->m_bDecodeFinished &&
                        avParam->m_aPktQueue->m_size == 0)
                        break;

                    /**
                     * 有音频的情况下，将视频同步到音频
                     * 跟音频的pts做对比，比视频快则做延时
                     */
                    audio_pts = avParam->m_aClock;
                }
                /** 没有音频的情况下，直接同步到外部时钟 */
                else {
                    audio_pts = (av_gettime() - avParam->m_startTime) / 1e6;
                    avParam->m_aClock = audio_pts;
                }

                //主要是 跳转的时候 我们把video_clock设置成0了
                //因此这里需要更新video_pts
                //否则当从后面跳转到前面的时候 会卡在这里
                video_pts = avParam->m_vClock;

                /* 视频播放比音频快，视频等待音频，即音视频同步 */
                if (video_pts <= audio_pts)
                    break;
                int delayTime = (video_pts - audio_pts) * 1000;
                delayTime = qMin(delayTime, 5);
                SDL_Delay(delayTime);
            }

            sws_scale(img_convert_ctx, (const uint8_t *const *)pFrame->data,
                      pFrame->linesize, 0, avParam->m_vCodecCtx->height,
                      pFrameRGB->data, pFrameRGB->linesize);

            /**
             * QImage构造函数 传进去的数据，他只是把指针指向了而已，
             * 如果数据在外部释放了，那么调用QImage就会出问题了，
             * 所以需要一次copy，copy是深度拷贝。
             *
             * 去掉透明的部分 有些奇葩的视频会透明
             */
            QImage tmpImg((uchar *)out_buffer_rgb, avParam->m_vCodecCtx->width,
                          avParam->m_vCodecCtx->height, QImage::Format_RGB32);
            //			QImage image = tmpImg.copy();
            QImage image =
                tmpImg.convertToFormat(QImage::Format_RGB888, Qt::NoAlpha);
            avParam->m_avPlayer->DisplayVideo(image); //调用激发信号的函数

            if (bFrist) {
                avParam->m_avPlayer->Pause();
                bFrist = false;
            }
        }

        av_packet_unref(packet);
    }

    av_free(pFrame);
    av_free(pFrameRGB);
    av_free(out_buffer_rgb);

    sws_freeContext(img_convert_ctx);

    avParam->m_bQuit = true;
    avParam->m_bVDecodeThreadFinished = true;

    qDebug() << __FUNCTION__ << "end";

    return 0;
}

/* 音频回调函数 */
void ACallback(void *userdata, Uint8 *stream, int len)
{
    QLsAVParam *avParam = (QLsAVParam *)userdata;

    int len1, audio_data_size;

    /* len是由SDL传入的SDL缓冲区的大小，如果这个缓冲未满，我们就一直往里填充数据
     */
    while (len > 0) {
        /* 没有解码出的数据，从数据包队列中解码出新的数据 */
        if (avParam->audio_buf_index >=
            avParam->audio_buf_size) { /* 解码新的数据 */
            audio_data_size = ADecode(avParam);

            if (avParam->audio_buf == nullptr)
                return;

            /* 没有解码出数据，默认播放静音 */
            if (audio_data_size < 0) {
                /* silence */
                avParam->audio_buf_size = 1024;
                /* 清零，静音 */
                memset(avParam->audio_buf, 0, avParam->audio_buf_size);
            } else
                avParam->audio_buf_size = audio_data_size;
            avParam->audio_buf_index = 0;
        }

        if (avParam->audio_buf == nullptr)
            return;

        /*  查看stream可用空间，决定一次copy多少数据，剩下的下次继续copy */
        len1 = qMin(avParam->audio_buf_size - avParam->audio_buf_index,
                    (quint32)len);

        /* 静音 */
        if (avParam->m_bMute)
            memset(avParam->audio_buf + avParam->audio_buf_index, 0, len1);
        /* 调节音量 */
        else
            RaiseVolume((char *)avParam->audio_buf + avParam->audio_buf_index,
                        len1, 1, avParam->m_volume);

        /* 填充SDL缓冲区，用于音频流播放 */
        memcpy(stream, avParam->audio_buf + avParam->audio_buf_index, len1);

        len -= len1;
        stream += len1;
        avParam->audio_buf_index += len1;
    }
}

/* 音频解码函数 */
int ADecode(QLsAVParam *avParam)
{
    AVPacket *packet = &avParam->m_aPacket; /* 音频数据 */

    int len2, decoded_data_size;
    qint64 dec_channel_layout;
    int wanted_nb_samples, resampled_data_size;

    for (;;) {
        /* ----- 操作响应 ----- */
        /* 停止 */
        if (avParam->m_bQuit) {
            avParam->m_bADecodeThreadFinished = true;
            avParam->m_aPktQueue->Clear();
            return -1;
        }

        /* 暂停 */
        if (avParam->m_bPause == true)
            return -1;

        /* ----- 音频数据包解析 ----- */
        /* 从队列里取出音频数据包 */
        if (!avParam->m_aPktQueue->PopFront(packet))
            return -1;

        /* 更新音频时钟 */
        if (packet->pts != AV_NOPTS_VALUE)
            avParam->m_aClock =
                av_q2d(avParam->m_aStream->time_base) * packet->pts;

        /* 清除音频解码器数据 */
        if (strcmp((char *)packet->data, FLUSH_DATA) == 0) {
            avcodec_flush_buffers(avParam->m_aCodecCtx);
            av_packet_unref(packet);
            continue;
        }

        /* 发生了跳转，则跳过关键帧到目的时间的这几帧 */
        if (avParam->m_bASeek) {
            if (avParam->m_aClock < avParam->m_seekTime)
                break;
            else
                avParam->m_bASeek = false;
        }

        /* ----- 解码音频数据 ----- */
        /* 解码一个音频数据包 */
        if (avcodec_send_packet(avParam->m_aCodecCtx,
                                packet)) { /* 解码出错，跳过这帧数据 */
            qDebug() << "m_aCodecCtx input AVPacket to decoder failed!";
            av_packet_unref(packet);
            continue;
        }

        /* 解码一包中的一帧或多帧数据 */
        resampled_data_size = 0;
        while (
            !avcodec_receive_frame(avParam->m_aCodecCtx, avParam->m_aFrame)) {
            /* 计算解码出来的桢需要的缓冲大小 */
            decoded_data_size = av_samples_get_buffer_size(
                nullptr, avParam->m_aFrame->channels,
                avParam->m_aFrame->nb_samples,
                (AVSampleFormat)avParam->m_aFrame->format, 1);

            /* 解码后的音频格式转换 */
            dec_channel_layout = (avParam->m_aFrame->channel_layout &&
                                  avParam->m_aFrame->channels ==
                                      av_get_channel_layout_nb_channels(
                                          avParam->m_aFrame->channel_layout))
                                     ? avParam->m_aFrame->channel_layout
                                     : av_get_default_channel_layout(
                                           avParam->m_aFrame->channels);
            wanted_nb_samples = avParam->m_aFrame->nb_samples;
            if (avParam->m_aFrame->format != avParam->audio_src_fmt ||
                dec_channel_layout != avParam->audio_src_channel_layout ||
                avParam->m_aFrame->sample_rate != avParam->audio_src_freq ||
                (wanted_nb_samples != avParam->m_aFrame->nb_samples &&
                 !avParam->m_aSwrCtx)) {
                if (avParam->m_aSwrCtx)
                    swr_free(&avParam->m_aSwrCtx);

                /* 音频播放参数固定 */
                avParam->m_aSwrCtx = swr_alloc();
                avParam->m_aSwrCtx = swr_alloc_set_opts(
                    avParam->m_aSwrCtx, avParam->audio_tgt_channel_layout,
                    (AVSampleFormat)avParam->audio_tgt_fmt,
                    avParam->audio_tgt_freq, dec_channel_layout,
                    (AVSampleFormat)avParam->m_aFrame->format,
                    avParam->m_aFrame->sample_rate, 0, nullptr);

                /* 使用指定的参数打开重采样器 */
                if (!avParam->m_aSwrCtx || swr_init(avParam->m_aSwrCtx) < 0) {
                    qDebug() << "swr_init() failed";
                    break;
                }

                avParam->audio_src_channel_layout = dec_channel_layout;
                avParam->audio_src_channels =
                    avParam->m_aStream->codecpar->channels;
                avParam->audio_src_freq =
                    avParam->m_aStream->codecpar->sample_rate;
                avParam->audio_src_fmt =
                    (AVSampleFormat)avParam->m_aStream->codecpar->format;
            }

            /* 这里我们可以对采样数进行调整，增加或者减少，一般可以用来做声画同步
             */
            if (avParam->m_aSwrCtx) {
                const uint8_t **in =
                    (const uint8_t **)avParam->m_aFrame->extended_data;
                uint8_t *out[] = { avParam->audio_buf2 };

                if (wanted_nb_samples !=
                    avParam->m_aFrame->nb_samples) { /* 补偿 */
                    if (swr_set_compensation(
                            avParam->m_aSwrCtx,
                            (wanted_nb_samples -
                             avParam->m_aFrame->nb_samples) *
                                avParam->audio_tgt_freq /
                                avParam->m_aFrame->sample_rate,
                            wanted_nb_samples * avParam->audio_tgt_freq /
                                avParam->m_aFrame->sample_rate) < 0) {
                        qDebug()
                            << __FUNCTION__ << "swr_set_compensation() failed";
                    }
                }

                len2 = swr_convert(
                    avParam->m_aSwrCtx, out,
                    sizeof(avParam->audio_buf2) / avParam->audio_tgt_channels /
                        av_get_bytes_per_sample(avParam->audio_tgt_fmt),
                    in, avParam->m_aFrame->nb_samples);

                if (len2 < 0) {
                    qDebug() << __FUNCTION__ << "swr_convert() failed";
                    break;
                }
                if (len2 == (int)sizeof(avParam->audio_buf2) /
                                avParam->audio_tgt_channels /
                                av_get_bytes_per_sample(avParam->audio_tgt_fmt))
                    swr_init(
                        avParam->m_aSwrCtx); /* 使用指定的参数打开重采样器 */

                avParam->audio_buf = avParam->audio_buf2;
                resampled_data_size +=
                    len2 * avParam->audio_tgt_channels *
                    av_get_bytes_per_sample(avParam->audio_tgt_fmt);
            } else {
                resampled_data_size += decoded_data_size;
                avParam->audio_buf = avParam->m_aFrame->data[0];
            }
        }

        /* 释放音频数据包缓存 */
        av_packet_unref(packet);

        /* 返回解码出的数据长度 */
        return resampled_data_size;
    }

    if (packet->data)
        av_packet_unref(packet);
    memset(packet, 0, sizeof(*packet));

    avParam->audio_pkt_data = packet->data;
    avParam->audio_pkt_size = packet->size;

    /* 返回解码出的数据长度 */
    return 0;
}

/* 音视频处理参数封装 */
QLsAVParam::QLsAVParam(QLsAVPlayer *avPlayer)
    : m_avPlayer(avPlayer) /* 音频播放器 */
      ,
      m_bFirstPause(true) /* 第一次显示是否暂停(为了加载完视频有个预览界面) */
      ,
      m_volume(1) /* 音量 [0, 1] */
{
}

QLsAVParam::~QLsAVParam()
{
}

/* 参数重置 */
void QLsAVParam::ResetParam()
{
    /* 音视频整体 */
    m_inputCtx = nullptr;
    m_bDecodeThreadFinished = false;
    m_bDecodeFinished = false;
    m_bQuit = false;
    m_bPause = false;
    m_bMute = m_bMute;
    m_bSeek = false;
    m_seekPos = 0;
    m_seekTime = 0;
    m_startTime = 0;
    m_pauseStartTime = 0;
    m_bFirstPause = m_bFirstPause;

    /* 视频 */
    m_vStreamIndex = -1;
    m_vCodecCtx = nullptr;
    m_vCodec = nullptr;
    m_vStream = nullptr;
    if (m_vPktQueue) {
        delete m_vPktQueue;
        m_vPktQueue = nullptr;
    }
    if (m_vDecodeThread.isRunning())
        m_vDecodeThread.waitForFinished();
    m_bVDecodeThreadFinished = false;
    m_vClock = 0;
    m_bVSeek = false;

    /* 音频 */
    m_aStreamIndex = -1;
    m_aCodecCtx = nullptr;
    m_aCodec = nullptr;
    m_aStream = nullptr;
    if (m_aPktQueue) {
        delete m_aPktQueue;
        m_aPktQueue = nullptr;
    }
    Q_UNUSED(m_aPacket)
    m_bADecodeThreadFinished = false;
    m_aFrame = nullptr;
    m_volume = m_volume;
    m_aClock = 0;
    m_bASeek = false;
    m_aFrameReSample = nullptr;
    m_aSwrCtx = nullptr;
}

/* ----- 音视频解码 ----- */
QLsAVPlayer::QLsAVPlayer(QObject *parent)
    : QObject(parent), m_thread(new QThread), m_avParam(new QLsAVParam(this)),
      m_playerStatus(Player_Stop), m_aDeviceID(-1)
{
    moveToThread(m_thread);
    m_thread->start();

    //	av_register_all();				/*
    //初始化FFMPEG，调用了这个才能正常使用编码器和解码器 */
    avformat_network_init(); /* 支持打开网络文件 */

    /* ---------- 多线程信号槽 ---------- */
    connect(this, &QLsAVPlayer::sigLoad, this, &QLsAVPlayer::slotLoad);
}

QLsAVPlayer::~QLsAVPlayer()
{
    Stop(true);

    m_thread->quit();
    m_thread->wait();
    delete m_thread;
    m_thread = nullptr;

    delete m_avParam;
    m_avParam = nullptr;
}

/* 解码 */
void QLsAVPlayer::Load()
{
    //	m_lockDecode.lock();
    //	const int nAvail = m_semDecode.available();
    //	if(nAvail > 0)
    //	{
    //		m_semDecode.tryAcquire(nAvail);
    //	}
    emit sigLoad();
    //	m_semDecode.tryAcquire(1, 5000);
    //	m_lockDecode.unlock();
}

/* 显示一帧图像 */
void QLsAVPlayer::DisplayVideo(QImage img)
{
    emit sigGetOneFrame(img);
}

/* 重新播放 */
bool QLsAVPlayer::Replay()
{
    if (m_playerStatus != Player_Stop)
        return false;

    Load();
    return true;
}

/* 播放 */
bool QLsAVPlayer::Play()
{
    m_avParam->m_bPause = false;

    if (m_playerStatus != Player_Pause)
        return false;
    m_playerStatus = Player_Playing;
    emit sigStatusChange(Player_Playing);

    return true;
}

/* 暂停 */
bool QLsAVPlayer::Pause()
{
    m_avParam->m_bPause = true;

    if (m_playerStatus != Player_Playing)
        return false;
    m_avParam->m_pauseStartTime = av_gettime();
    m_playerStatus = Player_Pause;
    emit sigStatusChange(Player_Pause);

    return true;
}

/* 停止 */
bool QLsAVPlayer::Stop(bool bWait)
{
    if (m_playerStatus == Player_Stop)
        return false;

    m_playerStatus = Player_Stop;
    m_avParam->m_bQuit = true;

    /* 等待解码线程执行完毕再返回 */
    if (bWait)
        while (!m_avParam->m_bDecodeThreadFinished)
            SDL_Delay(3);

    return true;
}

/* 跳转(微秒) */
void QLsAVPlayer::Seek(qint64 pos)
{
    if (m_avParam->m_bSeek)
        return;

    m_avParam->m_seekPos = pos;
    m_avParam->m_bSeek = true;
}

/* 静音 */
bool QLsAVPlayer::mute() const
{
    return m_avParam->m_bMute;
}

void QLsAVPlayer::setMute(const bool &bMute)
{
    m_avParam->m_bMute = bMute;
}

/* 音量 */
void QLsAVPlayer::SetVolume(const float &volume)
{
    m_avParam->m_volume = volume;
}

/* 路径 */
QString QLsAVPlayer::AVUrl() const
{
    return m_AVUrl;
}

void QLsAVPlayer::setAVUrl(const QString &AVUrl)
{
    m_AVUrl = AVUrl;
}

/* 音视频播放状态 */
QLsAVPlayer::QLsPlayerStatus QLsAVPlayer::playerStatus() const
{
    return m_playerStatus;
}

/* 播放进度/当前时间 */
qint64 QLsAVPlayer::curTime() const
{
    return m_avParam->m_aClock;
}

/* 总时长 */
qint64 QLsAVPlayer::totalTime() const
{
    return m_avParam->m_inputCtx->duration;
}

/* 设置是否显示预览(第一次加载视频是否暂停) */
void QLsAVPlayer::setPreview(const bool &bFirstPause)
{
    m_avParam->m_bFirstPause = bFirstPause;
}

/* 解码开始 */
void QLsAVPlayer::DecodeStart()
{
    qDebug() << __FUNCTION__ << QThread::currentThreadId();

    /* SDL初始化需要放入子线程中，否则有些电脑会有问题 */
    if (SDL_Init(SDL_INIT_AUDIO)) {
        qDebug() << "SDL_INIT_AUDIO error：" << SDL_GetError();
        return;
    }

    /* 重置参数 */
    m_avParam->ResetParam();
}

/* 解码结束 */
void QLsAVPlayer::DecodeEnd()
{
    m_avParam->m_aPktQueue->Clear();
    m_avParam->m_vPktQueue->Clear();

    /* 不是外部调用的 stop 是正常播放结束 */
    if (m_playerStatus != Player_Stop)
        Stop();

    /* 确保音视频线程结束后 再结束解码线程 */
    while ((m_avParam->m_vStream && !m_avParam->m_bVDecodeThreadFinished) ||
           (m_avParam->m_aStream && !m_avParam->m_bADecodeThreadFinished))
        QThread::msleep(10);

    /* SDL：暂停播放、关闭音频处理线程、关闭音频设备 */
    SDLCloseADevice();

    /* 视频 */
    if (m_avParam->m_vStreamIndex >= 0) {
        avcodec_close(m_avParam->m_vCodecCtx);
        if (m_avParam->m_vPktQueue) {
            delete m_avParam->m_vPktQueue;
            m_avParam->m_vPktQueue = nullptr;
        }
    }

    /* 音频 */
    if (m_avParam->m_aSwrCtx)
        swr_free(&m_avParam->m_aSwrCtx);
    if (m_avParam->m_aFrame)
        av_frame_free(&m_avParam->m_aFrame);
    if (m_avParam->m_aStreamIndex >= 0) {
        avcodec_close(m_avParam->m_aCodecCtx);
        if (m_avParam->m_aPktQueue) {
            delete m_avParam->m_aPktQueue;
            m_avParam->m_aPktQueue = nullptr;
        }
    }

    avformat_close_input(&m_avParam->m_inputCtx);
    avformat_free_context(m_avParam->m_inputCtx);

    SDL_Quit();

    m_avParam->m_bDecodeThreadFinished = true;

    emit sigStatusChange(Player_Stop);

    qDebug() << __FUNCTION__;
}

/* SDL打开音频设备 */
bool QLsAVPlayer::SDLOpenADevice()
{
    /* SDL音频规范 */
    SDL_AudioSpec wanted_spec, spec;

    /* 双通道 */
    qint64 wanted_channel_layout = av_get_default_channel_layout(2);
    wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    wanted_spec.channels =
        av_get_channel_layout_nb_channels(wanted_channel_layout);

    wanted_spec.freq = 44100;
    wanted_spec.format = AUDIO_S16SYS;
    wanted_spec.silence = 0;                     /* 静音 */
    wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; /* 自定义缓冲区 */
    wanted_spec.callback = ACallback;            /* 回调函数 */
    wanted_spec.userdata = m_avParam;            /* 回调参数 */

    /* 获取并打开音频播放设备 */
#if 1
    int num = SDL_GetNumAudioDevices(0);
    for (int i = 0; i < num; i++) {
        m_aDeviceID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(i, 0), false,
                                          &wanted_spec, &spec, 0);
        qDebug() << __FUNCTION__ << m_aDeviceID << SDL_GetAudioDeviceName(i, 0)
                 << QThread::currentThreadId();
        if (m_aDeviceID > 0)
            break;
    }
#else
    m_aDeviceID = SDL_OpenAudioDevice(nullptr, false, &wanted_spec, &spec,
                                      SDL_AUDIO_ALLOW_ANY_CHANGE);
    qDebug() << __FUNCTION__ << m_aDeviceID << QThread::currentThreadId();
#endif
    if (m_aDeviceID <= 0)
        return false;

    /* 检查实际使用的配置（保存在spec,由SDL_OpenAudio()填充） */
    if (spec.channels !=
        wanted_spec.channels) { /* 如果双通道不行，则使用实际配置 */
        wanted_channel_layout = av_get_default_channel_layout(spec.channels);
        if (!wanted_channel_layout) {
            fprintf(stderr, "SDL advised channel count %d is not supported!\n",
                    spec.channels);
            return false;
        }
    }

    /* 把设置好的参数保存到大结构中 */
    m_avParam->audio_hw_buf_size = spec.size;
    m_avParam->audio_src_fmt = m_avParam->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
    m_avParam->audio_src_freq = m_avParam->audio_tgt_freq = spec.freq;
    m_avParam->audio_src_channel_layout = m_avParam->audio_tgt_channel_layout =
        wanted_channel_layout;
    m_avParam->audio_src_channels = m_avParam->audio_tgt_channels =
        spec.channels;

    m_avParam->audio_buf_size = 0;
    m_avParam->audio_buf_index = 0;
    memset(&m_avParam->m_aPacket, 0, sizeof(m_avParam->m_aPacket));

    /* 播放音频 */
    SDL_LockAudioDevice(m_aDeviceID);
    SDL_PauseAudioDevice(m_aDeviceID, 0);
    SDL_UnlockAudioDevice(m_aDeviceID);

    return true;
}

/* SDL关闭音频设备 */
void QLsAVPlayer::SDLCloseADevice()
{
    if (m_aDeviceID > 0) {
        SDL_LockAudioDevice(m_aDeviceID);
        SDL_PauseAudioDevice(m_aDeviceID, 1);
        SDL_UnlockAudioDevice(m_aDeviceID);

        SDL_CloseAudioDevice(m_aDeviceID);
    }

    m_aDeviceID = -1;
}

/* 打开要解码的音视频流 */
bool QLsAVPlayer::AVOpenInput(const QString &url)
{
    m_avParam->m_inputCtx = avformat_alloc_context();

    /* 在打开码流前指定各种参数 */
    AVDictionary *opts = nullptr;
    av_dict_set(&opts, "rtsp_transport", "tcp",
                0); /* 设置tcp or udp，默认一般优先tcp再尝试udp */
    av_dict_set(&opts, "stimeout", "3000000", 0);  /* 设置超时3秒 */
    av_dict_set(&opts, "max_delay", "1000000", 0); /* 设置最大延时1s */
    //	av_dict_set(&opts, "buffer_size", "8192000", 0);	/* 设置缓存大小 */
    av_dict_set(&opts, "threads", "auto", 0); /* 自动开启线程数 */

    if (avformat_open_input(&m_avParam->m_inputCtx, url.toUtf8().data(),
                            nullptr, &opts)) {
        qDebug() << "打开视频流失败";
        return false;
    }

    /* 释放设置参数 */
    if (opts != NULL)
        av_dict_free(&opts);

    if (avformat_find_stream_info(m_avParam->m_inputCtx, nullptr) < 0) {
        qDebug() << "获取文件信息失败";
        return false;
    }
    return true;
}

/* 获取音视频流的位置信息 */
void QLsAVPlayer::AVGetStreamIndex()
{
    m_avParam->m_vStreamIndex = -1; /* 视频流位置 */
    m_avParam->m_aStreamIndex = -1; /* 音频流位置 */
    for (int i = 0; i < (int)m_avParam->m_inputCtx->nb_streams;
         i++) { /* 都获取到则跳出 */
        if (m_avParam->m_vStreamIndex != -1 && m_avParam->m_aStreamIndex != -1)
            break;
        /* 获取视频流位置 */
        if (m_avParam->m_inputCtx->streams[i]->codecpar->codec_type ==
                AVMEDIA_TYPE_VIDEO &&
            m_avParam->m_vStreamIndex < 0)
            m_avParam->m_vStreamIndex = i;
        /* 获取音频流位置 */
        if (m_avParam->m_inputCtx->streams[i]->codecpar->codec_type ==
                AVMEDIA_TYPE_AUDIO &&
            m_avParam->m_aStreamIndex < 0)
            m_avParam->m_aStreamIndex = i;
    }
    if (m_avParam->m_vStreamIndex == -1) {
        qDebug() << "没找到视频流";
    }
    if (m_avParam->m_aStreamIndex == -1) {
        qDebug() << "没找到音频流";
    }
}

/* 获取音频流信息 */
bool QLsAVPlayer::AVGetAStreamParam()
{
    if (m_avParam->m_aStreamIndex < 0)
        return true;

    /* 获取音频流 */
    m_avParam->m_aStream =
        m_avParam->m_inputCtx->streams[m_avParam->m_aStreamIndex];
    m_avParam->m_aStream->discard = AVDISCARD_DEFAULT; /* 丢弃无用的数据包 */

    /* 查找音频流解码器 */
    m_avParam->m_aCodecCtx = avcodec_alloc_context3(nullptr);
    avcodec_parameters_to_context(m_avParam->m_aCodecCtx,
                                  m_avParam->m_aStream->codecpar);
    m_avParam->m_aCodec =
        avcodec_find_decoder(m_avParam->m_aStream->codecpar->codec_id);
    /**
     * 2021-05-14 10:25：Could not update timestamps for skipped samples.
     * 没有此句会出现上述警告。
     */
    m_avParam->m_aCodecCtx->pkt_timebase = m_avParam->m_aStream->time_base;
    if (!m_avParam->m_aCodec) {
        qDebug() << "没有查到音频解码器";
        return false;
    }

    /* 打开音频流解码器 */
    if (avcodec_open2(m_avParam->m_aCodecCtx, m_avParam->m_aCodec, nullptr) <
        0) {
        qDebug() << "打开音频解码器失败";
        return false;
    }

    /* 音频流解码包缓冲区队列初始化 */
    m_avParam->m_aPktQueue = new QLsAVPacketQueue;

    /* 解码出的音频数据 */
    m_avParam->m_aFrame = av_frame_alloc();

    /* ----- 重采样设置选项 ----- */
    m_avParam->in_sample_fmt =
        m_avParam->m_aCodecCtx->sample_fmt; /* 输入的采样格式 */
    m_avParam->out_sample_fmt =
        AV_SAMPLE_FMT_S16; /* 输出的采样格式 16bit PCM */
    m_avParam->in_sample_rate =
        m_avParam->m_aCodecCtx->sample_rate; /* 输入的采样率 */
    m_avParam->out_sample_rate = 44100;      /* 输出的采样率 */
    m_avParam->in_ch_layout =
        m_avParam->m_aCodecCtx->channel_layout; /* 输入的声道布局 */
    m_avParam->out_ch_layout =
        av_get_default_channel_layout(2); /* 输出的声道布局 */
    m_avParam->out_ch_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
    /* wav/wmv 兼容 */
    if (m_avParam->in_ch_layout <= 0)
        m_avParam->in_ch_layout =
            av_get_default_channel_layout(m_avParam->m_aCodecCtx->channels);
    /* 音频播放参数固定 */
    m_avParam->m_aSwrCtx = swr_alloc();
    m_avParam->m_aSwrCtx = swr_alloc_set_opts(
        m_avParam->m_aSwrCtx, m_avParam->out_ch_layout,
        m_avParam->out_sample_fmt, m_avParam->out_sample_rate,
        m_avParam->in_ch_layout, m_avParam->in_sample_fmt,
        m_avParam->in_sample_rate, 0, nullptr);

    /* 使用指定的参数打开重采样器 */
    int ret = swr_init(m_avParam->m_aSwrCtx);
    if (ret < 0) {
        char buff[128] = { 0 };
        av_strerror(ret, buff, 128);

        qDebug() << __FUNCTION__ << "Could not open resample context" << buff;
        swr_free(&m_avParam->m_aSwrCtx);
        m_avParam->m_aSwrCtx = nullptr;
        return false;
    }

    /* SDL：打开音频设备、启动音频处理线程、开始播放 */
    if (!SDLOpenADevice()) {
        m_avParam->m_bADecodeThreadFinished = true;
    }

    return true;
}

/* 获取视频流信息 */
bool QLsAVPlayer::AVGetVStreamParam()
{
    if (m_avParam->m_vStreamIndex < 0)
        return false;

    /* 获取视频流 */
    m_avParam->m_vStream =
        m_avParam->m_inputCtx->streams[m_avParam->m_vStreamIndex];

    /* 查找视频流解码器 */
    m_avParam->m_vCodecCtx = avcodec_alloc_context3(nullptr);
    avcodec_parameters_to_context(m_avParam->m_vCodecCtx,
                                  m_avParam->m_vStream->codecpar);
    m_avParam->m_vCodec =
        avcodec_find_decoder(m_avParam->m_vCodecCtx->codec_id);
    if (!m_avParam->m_vCodec) {
        qDebug() << "没有查到视频解码器";
        return false;
    }
    /* 设置加速解码 */
    m_avParam->m_vCodecCtx->lowres = m_avParam->m_vCodec->max_lowres;
    m_avParam->m_vCodecCtx->flags2 |= AV_CODEC_FLAG2_FAST;

    /* 打开视频流解码器 */
    if (avcodec_open2(m_avParam->m_vCodecCtx, m_avParam->m_vCodec, nullptr) <
        0) {
        qDebug() << "打开视频解码器失败";
        return false;
    }

    /* 视频流解码包缓冲区队列初始化 */
    m_avParam->m_vPktQueue = new QLsAVPacketQueue;

    /* 创建视频解码线程 */
    m_avParam->m_vDecodeThread = QtConcurrent::run(VDecodeThread, m_avParam);

    return true;
}

/* 解码跳转 */
void QLsAVPlayer::AVDecodeSeek()
{
    if (!m_avParam->m_bSeek)
        return;

    /* 获取并计算时间戳 */
    int stream_index = -1;
    qint64 timestamp = m_avParam->m_seekPos;

    if (m_avParam->m_vStreamIndex >= 0)
        stream_index = m_avParam->m_vStreamIndex;
    else if (m_avParam->m_aStreamIndex >= 0)
        stream_index = m_avParam->m_aStreamIndex;

    AVRational aVRational = { 1, AV_TIME_BASE };
    if (stream_index >= 0) {
        timestamp = av_rescale_q(
            timestamp, aVRational,
            m_avParam->m_inputCtx->streams[stream_index]->time_base);
    }

    /* 定位时间戳附近的关键帧 */
    if (av_seek_frame(m_avParam->m_inputCtx, stream_index, timestamp,
                      AVSEEK_FLAG_BACKWARD) < 0)
        qDebug() << m_avParam->m_inputCtx->url << ": error while seeking";
    /* 定位到关键帧了，清空之前的数据包缓存 */
    else { /* 添加一个带有清空数据包缓存命令的数据包 */
        if (m_avParam->m_aStreamIndex >= 0) {
            AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
            av_new_packet(packet, 10);
            strcpy((char *)packet->data, FLUSH_DATA);
            m_avParam->m_aPktQueue->Clear();
            m_avParam->m_aPktQueue->Enqueue(packet);
        }
        if (m_avParam->m_vStreamIndex >= 0) {
            AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
            av_new_packet(packet, 10);
            strcpy((char *)packet->data, FLUSH_DATA);
            m_avParam->m_vPktQueue->Clear();
            m_avParam->m_vPktQueue->Enqueue(packet);
            m_avParam->m_vClock = 0;
        }

        m_avParam->m_startTime = av_gettime() - m_avParam->m_seekPos;
        m_avParam->m_pauseStartTime = av_gettime();
    }
    m_avParam->m_bSeek = false;
    m_avParam->m_seekTime = m_avParam->m_seekPos / 1e6;
    m_avParam->m_bASeek = true;
    m_avParam->m_bVSeek = true;
    m_avParam->m_bDecodeFinished = false;
}

/* 解码 */
void QLsAVPlayer::Decoding()
{
    /* 存放读取的数据包 */
    AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
    av_init_packet(packet);

    /* 播放状态初始化 */
    m_playerStatus = Player_Playing;
    emit sigStatusChange(Player_Playing);
    m_avParam->m_startTime = av_gettime();

    /* 循环读取数据包 */
    for (;;) {
        /* 停止 */
        if (m_avParam->m_bQuit)
            break;

        /* 跳转 */
        if (m_avParam->m_bSeek)
            AVDecodeSeek();

        /* 数据包队列 上限 */
        if (m_avParam->m_aPktQueue->m_size > MAX_AUDIO_SIZE ||
            m_avParam->m_vPktQueue->m_size > MAX_VIDEO_SIZE) {
            SDL_Delay(10);
            continue;
        }

        /* 暂停 */
        if (m_avParam->m_bPause == true) {
            SDL_Delay(10);
            continue;
        }

        /* 获取流的下一帧(未解码)数据 */
        if (av_read_frame(m_avParam->m_inputCtx, packet) < 0) {
            m_avParam->m_bDecodeFinished = true;

            /**
             * 音频线程结束不会将停止状态置位，视频线程结束才会将停止状态置位
             * 音视频播放情况下，就算音频结束了，也要等视频结束，将停止状态置位，才是真正的结束，所以继续延时等待
             * 只有音频的情况下，音频结束，整个工作也就结束了，所以下面要单独判断一下
             */
            if (m_avParam->m_aStreamIndex >= 0 &&
                m_avParam->m_vStreamIndex < 0 &&
                m_avParam->m_aPktQueue->m_size == 0) {
                m_avParam->m_bQuit = true;
                break;
            }

            /**
             * 解码完毕，但播放没结束，这个时候用户可能会有其他操作
             * 比如拖进度条等，所以不能直接 break
             * 这里的 continue 主要为了 seek 服务
             */
            SDL_Delay(10);
            continue;
        }

        /* 这里我们将数据存入队列 因此不调用 av_packet_unref 释放 */
        /* 视频数据包 */
        if (packet->stream_index == m_avParam->m_vStreamIndex)
            m_avParam->m_vPktQueue->Enqueue(packet);
        /* 音频数据包 */
        else if (packet->stream_index ==
                 m_avParam->m_aStreamIndex) { /* SDL打开失败，则直接释放 */
            if (m_avParam->m_bADecodeThreadFinished)
                av_packet_unref(packet);
            else
                m_avParam->m_aPktQueue->Enqueue(packet);
        }
        /* 其他，直接释放 */
        else
            av_packet_unref(packet);
    }

    free(packet);
}

/* 解码 的槽函数 */
void QLsAVPlayer::slotLoad()
{
    /* 解码开始 */
    DecodeStart();

    /* 打开要解码的音视频流 */
    if (!AVOpenInput(m_AVUrl))
        return;

    /* 获取音视频流的位置信息 */
    AVGetStreamIndex();

    /* 视频总时长加载 */
    emit sigTotalTimeChange(totalTime());

    /* 获取音视频流信息 */
    if (!AVGetAStreamParam())
        return;
    if (!AVGetVStreamParam())
        return;

    /* 解码 */
    Decoding();

    /* 等待播放完毕 */
    while (!m_avParam->m_bQuit)
        SDL_Delay(100);

    /* 解码结束 */
    DecodeEnd();
}
